
import axios from 'axios';

class Request {
    constructor(baseurl) {
        this._axios = axios.create({
            headers: { "X-Custom-UserAgent": `vbourse/v1.1.0` },
            // baseURL: "http://47.52.170.35:8889/dispark/tradingView",
            // baseURL: "http://192.168.123.172:8898/dispark/tradingView",
            // baseURL: "http://192.168.123.104:8898/public/tradingView",
            // baseURL: "http://47.52.170.35:8889/public/tradingView",
            baseURL: "http://wjwapi.moduointerface.com/public/tradingView",
        })
    }
    get(uri, params) {
        console.log(`GET网络请求=>${uri}|参数=>${JSON.stringify(params)}`)
        return this._axios.get(uri, params)
    }
    post(uri, params) {
        console.log(` post网络请求=>${uri}|参数=>${JSON.stringify(params)}`)
        return this._axios.post(uri, params)
    }
    send(uri, params) {
        console.log(`GET网络请求|参数|`, ...Array.prototype.slice.call(arguments))
        // return this._axios.get(...Array.prototype.slice.call(arguments))
        // return this._axios.get(uri, {
        //     'params': params
        // })
        return this._axios.post(uri, params)

    }
}

class Datafeeds {
    constructor() {
        this.serviceMap = {
            config: '/config',
            serverTime: '/timeStamp',
            symbolResolve: '/symbols',
            history: '/history',
            marks: '/marks',
            search: '/search',
            timescaleMarks: '/timescale_marks',
            quotes: '/quotes',
            symbolInfo: '/symbol_info'



        }
        this._symbolResolveURL = '/symbols';
        this._historyURL = '/history';
        this._enableLogging = true;
        this.request = new Request()
    }

    /**
        * 打印日志
        * @param {*} message 
        */
    log() {
        //日志打印
        if (this._enableLogging) {
            console.log(`日志|${new Date().getMilliseconds()}|`, ...Array.prototype.slice.call(arguments));
        }
    };


    /**
     * json转对象
     * @param {*} params 
     */
    jsonToObject(params) {
        if (typeof params === 'string') {
            return JSON.parse(params);
        } else {
            return params;
        }
    }


}
class UDFCompatibleDatafeed extends Datafeeds {
    constructor(updateFrequency) {
        super()
        this._configuration = undefined;

        this._symbolSearch = null;
        this._symbolsStorage = null;
        this._barsPulseUpdater = new DataPulseUpdater(this, updateFrequency || 10 * 1000);
        this._quotesPulseUpdater = new QuotesPulseUpdater(this);

        this._enableLogging = true;
        this._initializationFinished = false;
        this._callbacks = {};
        this.initialize();
    }


    /**
     * 默认的配置信息
     */
    defaultConfiguration() {
        return {
            "exchanges": [{ "desc": "", "name": "All Exchanges", "value": "" }],
            "supports_time": true,
            "supports_group_request": false,
            "supports_search": true,
            "supports_marks": false,
            "supports_timescale_marks": false,
            "supported_resolutions": ["1", "3", "5", "10", "15", "30", "60", "120", "240", "360", "720", "D", "7D"],
            "symbols_types": [{ "name": "All types", "value": "" }]
        }
    };
    /**
     * 默认的市场信息
     * @param {*} symbol 
     */
    defaultSymbols(symbol) {
        return {
            'name': symbol,
            'timezone': "Asia/Shanghai",
            'has_intraday': true,
            'has_no_volume': false,
            'description': '',
            'supported_resolutions': ['1', '3', '5', '10', '15', '30', '60', '120', '240', '360', '720', 'D', '7D'],
            'pricescale': 100,
            'ticker': symbol,
        };
    };

    /**
  * 初始化
  */
    initialize() {
        //拉取服务端默认配置
        const result = this.request.send(this.serviceMap.config)
        result.then((response) => {
            this.log('获取服务端配置成功!', response.data.body);
            this.setupWithConfiguration(this.jsonToObject(response.data.body));
        })
        result.catch((reason) => {
            this.log('获取服务端配置适配失败,设置为默认配置');
            this.setupWithConfiguration(this.jsonToObject(this.defaultConfiguration()));
        });
    };



    /**
     * 获取服务器信息
     * @param {*} callback 
     */
    getServerTime(callback) {
        //获取服务器时间方法
        if (this._configuration.supports_time) {
            const result = this.request.send(this.serviceMap.serverTime)
            result.then((response) => {
                let time = +response.data.body;
                if (!isNaN(time)) {
                    callback(time);
                }
            })
            result.catch(() => {
            });
        }
    };

    /**
     * 监听
     * @param {*} event 
     * @param {*} callback 
     */
    on(event, callback) {
        if (!this._callbacks.hasOwnProperty(event)) {
            this._callbacks[event] = [];
        }
        this._callbacks[event].push(callback);
        return this;
    };
    /**
     * 发布
     * @param {*} event 
     * @param {*} argument 
     */
    emit(event, argument) {
        if (this._callbacks.hasOwnProperty(event)) {
            let callbacksChain = this._callbacks[event];
            for (let i = 0; i < callbacksChain.length; ++i) {
                callbacksChain[i](argument);
            }
            this._callbacks[event] = [];
        }
    };

    /**
     * 初始化事件
     */
    onInitialized() {
        //发送初始化完成事件
        this._initializationFinished = true;
        this.emit('initialized');
    };


    onReady(callback) {
        //加载完成
        let that = this;
        if (this._configuration) {
            setTimeout(function () {
                callback(that._configuration);
            }, 0);
        } else {
            this.on('configuration_ready', function () {
                callback(that._configuration);
            });
        }
    };

    setupWithConfiguration(configurationData) {
        this._configuration = configurationData;
        if (!configurationData.exchanges) {
            configurationData.exchanges = [];
        }

        //	@obsolete; remove in 1.5
        let supportedResolutions = configurationData.supported_resolutions || configurationData.supportedResolutions;
        configurationData.supported_resolutions = supportedResolutions;

        //	@obsolete; remove in 1.5
        let symbolsTypes = configurationData.symbols_types || configurationData.symbolsTypes;
        configurationData.symbols_types = symbolsTypes;

        if (!configurationData.supports_search && !configurationData.supports_group_request) {
            throw new Error('Unsupported datafeed configuration. Must either support search, or support group request');
        }

        if (!configurationData.supports_search) {
            this._symbolSearch = new SymbolSearchComponent(this);
        }

        if (configurationData.supports_group_request) {
            //	this component will call onInitialized() by itself
            this._symbolsStorage = new SymbolsStorage(this);
        } else {
            this.onInitialized();
        }

        this.emit('configuration_ready');
        this.log('Initialized with ' + JSON.stringify(configurationData));
    };
    //	===============================================================================================================================
    //	下面设置的功能是JavaScript API的实现。
    getMarks(symbolInfo, rangeStart, rangeEnd, onDataCallback, resolution) {

        if (this._configuration.supports_marks) {
            const result = this.request.send(this.serviceMap.marks, {

                symbol: symbolInfo.ticker.toUpperCase(),
                from: rangeStart,
                to: rangeEnd,
                resolution: resolution


            })
            result.then((response) => {
                this.log(`marks请求成功，结果|`, response)
                onDataCallback(this.jsonToObject(response.data.body));
            })
            result.catch(() => {
                onDataCallback([]);
            });
        }
    };

    getTimescaleMarks(symbolInfo, rangeStart, rangeEnd, onDataCallback, resolution) {
        //获取指定时间的市场数据
        if (this._configuration.supports_timescale_marks) {
            const result = this.request.send(this.serviceMap.timescaleMarks, {

                symbol: symbolInfo.ticker.toUpperCase(),
                from: rangeStart,
                to: rangeEnd,
                resolution: resolution


            })
            result.then((response) => {
                this.log(`timescale_marks请求成功，结果|`, response)
                onDataCallback(this.jsonToObject(response.data.body));
            })
            result.catch(() => {
                onDataCallback([]);
            });
        }
    };

    searchSymbols(searchString, exchange, type, onResultReadyCallback) {
        //搜索市场
        let MAX_SEARCH_RESULTS = 30;
        if (!this._configuration) {
            onResultReadyCallback([]);
            return;
        }

        if (this._configuration.supports_search) {
            const result = this.request.send(this.serviceMap.search, {

                limit: MAX_SEARCH_RESULTS,
                query: searchString.toUpperCase(),
                type: type,
                exchange: exchange


            })
            result.then((response) => {
                let data = this.jsonToObject(response.data.body);
                for (let i = 0; i < data.length; ++i) {
                    if (!data[i].params) {
                        data[i].params = [];
                    }
                    data[i].exchange = data[i].exchange || '';
                }
                if (typeof data.s == 'undefined' || data.s !== 'error') {
                    onResultReadyCallback(data);
                } else {
                    onResultReadyCallback([]);
                }
            })
            result.catch((reason) => {
                onResultReadyCallback([]);
            });

        } else {
            if (!this._symbolSearch) {
                throw new Error('Datafeed error: inconsistent configuration (symbol search)');
            }

            let searchArgument = {
                searchString: searchString,
                exchange: exchange,
                type: type,
                onResultReadyCallback: onResultReadyCallback
            };

            if (this._initializationFinished) {
                this._symbolSearch.searchSymbols(searchArgument, MAX_SEARCH_RESULTS);
            } else {
                this.on('initialized', () => {
                    this._symbolSearch.searchSymbols(searchArgument, MAX_SEARCH_RESULTS);
                });
            }
        }
    };




    resolveSymbol(symbolName, onSymbolResolvedCallback, onResolveErrorCallback) {
        let that = this;
        this.log('未知方法被调用resolveSymbol' + symbolName);

        if (!this._initializationFinished) {
            this.log('初始化未完成');
            this.on('initialized', function () {
                that.log('初始化完成,处理resolveSymbol' + symbolName);
                that.resolveSymbol(symbolName, onSymbolResolvedCallback, onResolveErrorCallback);
            });

            return;
        }

        let resolveRequestStartTime = Date.now();

        function onResultReady(data) {
            let postProcessedData = data;
            if (that.postProcessSymbolInfo) {
                postProcessedData = that.postProcessSymbolInfo(postProcessedData);
            }
            onSymbolResolvedCallback(postProcessedData);
        }

        if (!this._configuration.supports_group_request) {
            this.log('请求图表数据', symbolName);

            const result = this.request.send(this.serviceMap.symbolResolve, {
                symbol: symbolName ? symbolName.toUpperCase() : ''
            })
            result.then((response) => {
                let data = this.jsonToObject(response.data.body);
                if (data.s && data.s !== 'ok') {
                    that.log('请求图表数据失败,设置为本地默认', that.defaultSymbols(symbolName ? symbolName.toUpperCase() : ''));
                    onResultReady(that.defaultSymbols(symbolName ? symbolName.toUpperCase() : ''));
                } else {
                    that.log('请求图表数据成功');
                    onResultReady(data);
                }
            })
            result.catch((reason) => {
                that.log('请求图表数据失败fail,使用默认配置');
                onResultReady(that.defaultSymbols(symbolName ? symbolName.toUpperCase() : ''));
            });

        } else {
            if (this._initializationFinished) {
                this._symbolsStorage.resolveSymbol(symbolName, onResultReady, onResolveErrorCallback);
            } else {
                this.on('initialized', function () {
                    that._symbolsStorage.resolveSymbol(symbolName, onResultReady, onResolveErrorCallback);
                });
            }
        }


    };

    getBars(symbolInfo, resolution, rangeStartDate, rangeEndDate, onDataCallback, onErrorCallback) {

        if (rangeStartDate > 0 && (rangeStartDate + '').length > 10) {
            throw new Error(['Got a JS time instead of Unix one.', rangeStartDate, rangeEndDate]);
        }
        const result = this.request.send(this.serviceMap.history, {
            symbol: symbolInfo.ticker.toUpperCase(),
            resolution: resolution,
            from: rangeStartDate,
            to: rangeEndDate
        })
        result.then((response) => {
            let data = this.jsonToObject(response.data.body);
            let nodata = data.s === 'no_data';

            if (data.s !== 'ok' && !nodata) {
                if (!!onErrorCallback) {
                    onErrorCallback(data.s);
                }

                return;
            }

            let bars = [];

            //	data is JSON having format {s: "status" (ok, no_data, error),
            //  v: [volumes], t: [times], o: [opens], h: [highs], l: [lows], c:[closes], nb: "optional_unixtime_if_no_data"}
            let barsCount = nodata ? 0 : data.t.length;

            let volumePresent = typeof data.v != 'undefined';
            let ohlPresent = typeof data.o != 'undefined';

            for (let i = 0; i < barsCount; ++i) {
                let barValue = {
                    time: data.t[i],
                    close: +data.c[i]
                };

                if (ohlPresent) {
                    barValue.open = +data.o[i];
                    barValue.high = +data.h[i];
                    barValue.low = +data.l[i];
                } else {
                    barValue.open = barValue.high = barValue.low = +barValue.close;
                }

                if (volumePresent) {
                    barValue.volume = +data.v[i];
                }

                bars.push(barValue);
            }
            onDataCallback(bars, { noData: nodata, nextTime: data.nb || data.nextTime });
        })
        result.catch((arg) => {
            if (!!onErrorCallback) {
                onErrorCallback('network error: ' + JSON.stringify(arg));
            }
        });
    };
    subscribeBars(symbolInfo, resolution, onRealtimeCallback, listenerGUID, onResetCacheNeededCallback) {
        this.log('未知方法被调用 subscribeBars');
        this._barsPulseUpdater.subscribeDataListener(symbolInfo, resolution, onRealtimeCallback, listenerGUID, onResetCacheNeededCallback);
    };
    unsubscribeBars(listenerGUID) {
        this.log('未知方法被调用 unsubscribeBars');
        this._barsPulseUpdater.unsubscribeDataListener(listenerGUID);
    };
    calculateHistoryDepth(period, resolutionBack, intervalBack) {
        this.log('计算历史深度 calculateHistoryDepth');
    };
    getQuotes(symbols, onDataCallback, onErrorCallback) {
        this.log('未知方法被调用 getQuotes');
        const result = this.request.send(this.serviceMap.quotes, { symbols: symbols })
        result.then((response) => {
            let data = this.jsonToObject(response.data.body);
            if (data.s === 'ok') {
                //	JSON format is {s: "status", [{s: "symbol_status", n: "symbol_name", v: {"field1": "value1", "field2": "value2", ..., "fieldN": "valueN"}}]}
                if (onDataCallback) {
                    onDataCallback(data.d);
                }
            } else {
                if (onErrorCallback) {
                    onErrorCallback(data.errmsg);
                }
            }
        })
        result.catch((arg) => {
            if (onErrorCallback) {
                onErrorCallback('network error: ' + arg);
            }
        });
    };

    subscribeQuotes(symbols, fastSymbols, onRealtimeCallback, listenerGUID) {
        this.log('未知方法被调用 subscribeQuotes');
        this._quotesPulseUpdater.subscribeDataListener(symbols, fastSymbols, onRealtimeCallback, listenerGUID);
    };
    unsubscribeQuotes(listenerGUID) {
        this.log('未知方法被调用 unsubscribeQuotes');
        this._quotesPulseUpdater.unsubscribeDataListener(listenerGUID);
    };

    //	==================================================================================================================================================

    /*
        它是ExternalDatafeed的符号存储组件。 这个组件可以
            *与支持整个组信息请求的UDF兼容数据传输交互
            *做符号解析 - 通过名称返回符号信息
    */



}

/**
 * 获取市场所有的对象 并存储
 */
class SymbolsStorage extends Datafeeds {
    constructor(datafeed) {
        super()
        this._datafeed = datafeed;
        this._exchangesList = ['NYSE', 'FOREX', 'AMEX'];
        this._exchangesWaitingForData = {};
        this._exchangesDataCache = {};
        this._symbolsInfo = {};
        this._symbolsList = [];
        this.requestFullSymbolsList();
    }
    defaultSymbols_info(exchange) {
        return {
            'symbol': 'WKB-WCNY', //市场
            'description': 'WKB-WCNY', //描述
            'has-dwm': true,
            'has-intraday': true,
            'supported_resolutions': ['1', '3', '5', '10', '15', '30', '60', '120', '240', '360', '720', 'D', '7D']

        };
    };
    /**
     * 获取全部的Symbols列表
     */
    requestFullSymbolsList() {
        let that = this;
        for (let i = 0; i < this._exchangesList.length; ++i) {
            let exchange = this._exchangesList[i];

            if (this._exchangesDataCache.hasOwnProperty(exchange)) {
                continue;
            }

            this._exchangesDataCache[exchange] = true;

            this._exchangesWaitingForData[exchange] = 'waiting_for_data';

            const result = this.request.send(this.serviceMap.symbolInfo, {
                group: exchange
            })
            result.then((function (exchange) {
                console.log('symbol_info获取成功')
                return function (response) {
                    that.onExchangeDataReceived(exchange, that.jsonToObject(response.data.body));
                    that.onAnyExchangeResponseReceived(exchange);
                };
            })(exchange))

            result.catch((function (exchange) {
                console.log('symbol_info获取失败 设置为本地模式')
                return function (reason) {
                    console.log('symbol_info获取失败 设置为本地模式', that.defaultSymbols_info(exchange))
                    that.onExchangeDataReceived(exchange, that.jsonToObject(that.defaultSymbols_info(exchange)));
                    that.onAnyExchangeResponseReceived(exchange);
                };
            })(exchange));
        }
    };

    /**
    * 接收处理Exchange数据
    */
    onExchangeDataReceived(exchangeName, data) {
        function tableField(data, name, index) {
            return data[name] instanceof Array ?
                data[name][index] :
                data[name];
        }
        try {
            for (let symbolIndex = 0; symbolIndex < data.symbol.length; ++symbolIndex) {
                let symbolName = data.symbol[symbolIndex];
                let listedExchange = tableField(data, 'exchange-listed', symbolIndex);
                let tradedExchange = tableField(data, 'exchange-traded', symbolIndex);
                let fullName = tradedExchange + ':' + symbolName;

                //	This feature support is not implemented yet
                //	let hasDWM = tableField(data, "has-dwm", symbolIndex);

                let hasIntraday = tableField(data, 'has-intraday', symbolIndex);

                let tickerPresent = typeof data.ticker != 'undefined';

                let symbolInfo = {
                    name: symbolName,
                    base_name: [listedExchange + ':' + symbolName],
                    description: tableField(data, 'description', symbolIndex),
                    full_name: fullName,
                    legs: [fullName],
                    has_intraday: hasIntraday,
                    has_no_volume: tableField(data, 'has-no-volume', symbolIndex),
                    listed_exchange: listedExchange,
                    exchange: tradedExchange,
                    minmov: tableField(data, 'minmovement', symbolIndex) || tableField(data, 'minmov', symbolIndex),
                    minmove2: tableField(data, 'minmove2', symbolIndex) || tableField(data, 'minmov2', symbolIndex),
                    fractional: tableField(data, 'fractional', symbolIndex),
                    pointvalue: tableField(data, 'pointvalue', symbolIndex),
                    pricescale: tableField(data, 'pricescale', symbolIndex),
                    type: tableField(data, 'type', symbolIndex),
                    session: tableField(data, 'session-regular', symbolIndex),
                    ticker: tickerPresent ? tableField(data, 'ticker', symbolIndex) : symbolName,
                    timezone: tableField(data, 'timezone', symbolIndex),
                    supported_resolutions: tableField(data, 'supported-resolutions', symbolIndex) || this._datafeed.defaultConfiguration().supported_resolutions,
                    force_session_rebuild: tableField(data, 'force-session-rebuild', symbolIndex) || false,
                    has_daily: tableField(data, 'has-daily', symbolIndex) || true,
                    intraday_multipliers: tableField(data, 'intraday-multipliers', symbolIndex) || ['1', '5', '15', '30', '60'],
                    has_weekly_and_monthly: tableField(data, 'has-weekly-and-monthly', symbolIndex) || false,
                    has_empty_bars: tableField(data, 'has-empty-bars', symbolIndex) || false,
                    volume_precision: tableField(data, 'volume-precision', symbolIndex) || 0
                };

                this._symbolsInfo[symbolInfo.ticker] = this._symbolsInfo[symbolName] = this._symbolsInfo[fullName] = symbolInfo;
                this._symbolsList.push(symbolName);
            }
        } catch (error) {
            throw new Error('API error when processing exchange `' + exchangeName + '` symbol #' + symbolIndex + ': ' + error);
        }
    };
    /**
     * 响应所有的Exchange
     * @param {*} exchangeName 
     */
    onAnyExchangeResponseReceived(exchangeName) {
        // this.log('未知方法被调用 onAnyExchangeResponseReceived');
        delete this._exchangesWaitingForData[exchangeName];

        let allDataReady = Object.keys(this._exchangesWaitingForData).length === 0;

        if (allDataReady) {
            this._symbolsList.sort();
            this._datafeed.log('All exchanges data ready');
            this._datafeed.onInitialized();
        }
    };
    /**
     * 解析Symbol信息
     * @param {*} symbolName 
     * @param {*} onSymbolResolvedCallback 
     * @param {*} onResolveErrorCallback 
     */
    resolveSymbol(symbolName, onSymbolResolvedCallback, onResolveErrorCallback) {
        console.log('dasdsdsadsadasd============', symbolName, onSymbolResolvedCallback, onResolveErrorCallback)
        let that = this;
        setTimeout(function () {
            if (!that._symbolsInfo.hasOwnProperty(symbolName)) {
                onResolveErrorCallback('invalid symbol');
            } else {
                onSymbolResolvedCallback(that._symbolsInfo[symbolName]);
            }
        }, 0);


    };
}

/**
 *  市场搜索
 */
class SymbolSearchComponent extends Datafeeds {
    constructor(datafeed) {
        super()
        this._datafeed = datafeed;
    }
    searchSymbols(searchArgument, maxSearchResults) {
        if (!this._datafeed._symbolsStorage) {
            throw new Error('Cannot use local symbol search when no groups information is available');
        }

        let symbolsStorage = this._datafeed._symbolsStorage;

        let results = []; // array of WeightedItem { item, weight }
        let queryIsEmpty = !searchArgument.searchString || searchArgument.searchString.length === 0;
        let searchStringUpperCase = searchArgument.searchString.toUpperCase();

        for (let i = 0; i < symbolsStorage._symbolsList.length; ++i) {
            let symbolName = symbolsStorage._symbolsList[i];
            let item = symbolsStorage._symbolsInfo[symbolName];

            if (searchArgument.type && searchArgument.type.length > 0 && item.type !== searchArgument.type) {
                continue;
            }

            if (searchArgument.exchange && searchArgument.exchange.length > 0 && item.exchange !== searchArgument.exchange) {
                continue;
            }

            let positionInName = item.name.toUpperCase().indexOf(searchStringUpperCase);
            let positionInDescription = item.description.toUpperCase().indexOf(searchStringUpperCase);

            if (queryIsEmpty || positionInName >= 0 || positionInDescription >= 0) {
                let found = false;
                for (let resultIndex = 0; resultIndex < results.length; resultIndex++) {
                    if (results[resultIndex].item === item) {
                        found = true;
                        break;
                    }
                }

                if (!found) {
                    let weight = positionInName >= 0 ? positionInName : 8000 + positionInDescription;
                    results.push({ item: item, weight: weight });
                }
            }
        }

        searchArgument.onResultReadyCallback(
            results
                .sort(function (weightedItem1, weightedItem2) {
                    return weightedItem1.weight - weightedItem2.weight;
                })

                .map(function (weightedItem) {
                    let item = weightedItem.item;
                    return {
                        symbol: item.name,
                        full_name: item.full_name,
                        description: item.description,
                        exchange: item.exchange,
                        params: [],
                        type: item.type,
                        ticker: item.name
                    };
                })
                .slice(0, Math.min(results.length, maxSearchResults))
        );
    };

}
/**
 * 数据更新
 */
class DataPulseUpdater extends Datafeeds {
    constructor(datafeed, updateFrequency) {
        super()
        this._datafeed = datafeed;
        this._subscribers = {};

        this._requestsPending = 0;
        let that = this;

        let update = function () {
            if (that._requestsPending > 0) {
                return;
            }

            for (let listenerGUID in that._subscribers) {
                let subscriptionRecord = that._subscribers[listenerGUID];
                let resolution = subscriptionRecord.resolution;

                let datesRangeRight = parseInt((new Date().valueOf()) / 1000);

                //	BEWARE: please note we really need 2 bars, not the only last one
                //	see the explanation below. `10` is the `large enough` value to work around holidays
                let datesRangeLeft = datesRangeRight - that.periodLengthSeconds(resolution, 10);

                that._requestsPending++;

                (function (_subscriptionRecord) { // eslint-disable-line
                    that._datafeed.getBars(_subscriptionRecord.symbolInfo, resolution, datesRangeLeft, datesRangeRight, function (bars) {
                        that._requestsPending--;

                        //	means the subscription was cancelled while waiting for data
                        if (!that._subscribers.hasOwnProperty(listenerGUID)) {
                            return;
                        }

                        if (bars.length === 0) {
                            return;
                        }

                        let lastBar = bars[bars.length - 1];
                        if (!isNaN(_subscriptionRecord.lastBarTime) && lastBar.time < _subscriptionRecord.lastBarTime) {
                            return;
                        }

                        let subscribers = _subscriptionRecord.listeners;

                        //	BEWARE: this one isn't working when first update comes and this update makes a new bar. In this case
                        //	_subscriptionRecord.lastBarTime = NaN
                        let isNewBar = !isNaN(_subscriptionRecord.lastBarTime) && lastBar.time > _subscriptionRecord.lastBarTime;

                        //	Pulse updating may miss some trades data (ie, if pulse period = 10 secods and new bar is started 5 seconds later after the last update, the
                        //	old bar's last 5 seconds trades will be lost). Thus, at fist we should broadcast old bar updates when it's ready.
                        if (isNewBar) {
                            if (bars.length < 2) {
                                throw new Error('Not enough bars in history for proper pulse update. Need at least 2.');
                            }

                            let previousBar = bars[bars.length - 2];
                            for (let i = 0; i < subscribers.length; ++i) {
                                subscribers[i](previousBar);
                            }
                        }

                        _subscriptionRecord.lastBarTime = lastBar.time;

                        for (let i = 0; i < subscribers.length; ++i) {
                            subscribers[i](lastBar);
                        }
                    },
                        //	on error
                        function () {
                            that._requestsPending--;
                        });
                })(subscriptionRecord);
            }
        };

        if (typeof updateFrequency != 'undefined' && updateFrequency > 0) {
            setInterval(update, updateFrequency);
        }
    }
    unsubscribeDataListener(listenerGUID) {
        this._datafeed.log('Unsubscribing ' + listenerGUID);
        delete this._subscribers[listenerGUID];
    };
    subscribeDataListener(symbolInfo, resolution, newDataCallback, listenerGUID) {
        this._datafeed.log('Subscribing ' + listenerGUID);

        if (!this._subscribers.hasOwnProperty(listenerGUID)) {
            this._subscribers[listenerGUID] = {
                symbolInfo: symbolInfo,
                resolution: resolution,
                lastBarTime: NaN,
                listeners: []
            };
        }

        this._subscribers[listenerGUID].listeners.push(newDataCallback);
    };

    periodLengthSeconds(resolution, requiredPeriodsCount) {
        let daysCount = 0;
        if (resolution === 'D') {
            daysCount = requiredPeriodsCount;
        } else if (resolution === 'M') {
            daysCount = 31 * requiredPeriodsCount;
        } else if (resolution === 'W') {
            daysCount = 7 * requiredPeriodsCount;
        } else {
            daysCount = requiredPeriodsCount * resolution / (24 * 60);
        }
        return daysCount * 24 * 60 * 60;
    };
}
/**
 * 行情更新
 */
class QuotesPulseUpdater extends Datafeeds {
    constructor(datafeed) {
        super()
        this._datafeed = datafeed;
        this._subscribers = {};
        this._updateInterval = 60 * 1000;
        this._fastUpdateInterval = 10 * 1000;
        this._requestsPending = 0;

        let that = this;

        setInterval(function () {
            that._updateQuotes(function (subscriptionRecord) { return subscriptionRecord.symbols; });
        }, this._updateInterval);

        setInterval(function () {
            that._updateQuotes(function (subscriptionRecord) { return subscriptionRecord.fastSymbols.length > 0 ? subscriptionRecord.fastSymbols : subscriptionRecord.symbols; });
        }, this._fastUpdateInterval);
    }

    subscribeDataListener(symbols, fastSymbols, newDataCallback, listenerGUID) {
        if (!this._subscribers.hasOwnProperty(listenerGUID)) {
            this._subscribers[listenerGUID] = {
                symbols: symbols,
                fastSymbols: fastSymbols,
                listeners: []
            };
        }

        this._subscribers[listenerGUID].listeners.push(newDataCallback);
    };
    unsubscribeDataListener(listenerGUID) {
        delete this._subscribers[listenerGUID];
    };
    _updateQuotes(symbolsGetter) {
        if (this._requestsPending > 0) {
            return;
        }
        let that = this;
        for (let listenerGUID in this._subscribers) {
            this._requestsPending++;

            let subscriptionRecord = this._subscribers[listenerGUID];
            this._datafeed.getQuotes(symbolsGetter(subscriptionRecord),

                // onDataCallback
                (function (subscribers, guid) { // eslint-disable-line
                    return function (data) {
                        that._requestsPending--;

                        // means the subscription was cancelled while waiting for data
                        if (!that._subscribers.hasOwnProperty(guid)) {
                            return;
                        }

                        for (let i = 0; i < subscribers.length; ++i) {
                            subscribers[i](data);
                        }
                    };
                }(subscriptionRecord.listeners, listenerGUID)),
                // onErrorCallback
                function (error) {
                    that._requestsPending--;
                });
        }
    };
}


if (typeof module !== 'undefined' && module && module.exports) {
    module.exports = {
        UDFCompatibleDatafeed,
    };
}


export default UDFCompatibleDatafeed
